package 剑指offer;
//0728
/**
 * 由于只需要找出数组中任意一个重复的数字，因此遍历数组，遇到重复的数字即返回。为了判断一个数字是否重复遇到，使用集合存储已经遇到的数字，如果遇到的一个数字已经在集合中，则当前的数字是重复数字。
 *
 * 初始化集合为空集合，重复的数字 repeat = -1
 * 遍历数组中的每个元素：
 * 将该元素加入集合中，判断是否添加成功
 * 如果添加失败，说明该元素已经在集合中，因此该元素是重复元素，将该元素的值赋给 repeat，并结束遍历
 * 返回 repeat
 *
 * 作者：LeetCode-Solution
 * 链接：https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-b-4/
 * 来源：力扣（LeetCode）
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */

import java.util.HashSet;
import java.util.Set;

/**
 * 找出数组中重复的数字。
 * 在一个长度为 n 的数组 nums 里的所有数字都在 0～n-1 的范围内。数组中某些数字是重复的，但不知道有几个数字重复了，也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
 * 示例 1：
 * 输入：
 * [2, 3, 1, 0, 2, 5, 3]
 * 输出：2 或 3
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
public class 数组中重复的数字 {//o(n)
    public static int findRepeatNumber(int[] nums) {
        Set<Integer> set=new HashSet<>();
        int repeat=0;
        for (int i:nums){
            if(!set.add(i)){
                repeat=i;
                break;
            }
        }
        return repeat;
    }

    public static void main(String[] args) {
        System.out.println(findRepeatNumber(new int[]{1,2,2,3,3,5,6}));
    }
}
/*
* 复杂性分析

时间复杂度：O(n)O(n)。
遍历数组一遍。使用哈希集合（HashSet），添加元素的时间复杂度为 O(1)O(1)，故总的时间复杂度是 O(n)O(n)。
空间复杂度：O(n)O(n)。不重复的每个元素都可能存入集合，因此占用 O(n)O(n) 额外空间

* */
/*

 public int findRepeatNumber(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] == nums[j]) {
                return nums[i];
            }
        }
    }
    return -1;
    时间复杂度是 O(n^2)
    空间复杂度是 O(1)
}

public int findRepeatNumber(int[] nums) {
    // 1. 初始化一个哈希表
    Set<Integer> set = new HashSet<>();
    for (int i = 0; i < nums.length; i++) {
        // 2. 判断当前元素是否已经存在
        if (set.contains(nums[i])) {
            // 如果存在，则直接返回
            return nums[i];
        }
​
        // 否则的话，将当前元素放入到哈希表中，方便后续的查找判重
        set.add(nums[i]);
    }
    return -1;
       时间复杂度是 O(n)
        空间复杂度是 O(n)
}

public int findRepeatNumber(int[] nums) {
    // 1. 初始化一个数组
    int[] bucket = new int[nums.length];
    Arrays.fill(bucket, -1);
​
    for (int i = 0; i < nums.length; i++) {
        // 2. 判断当前元素是否已经存在
        if (bucket[nums[i]] != -1) {
            // 如果存在，则直接返回
            return nums[i];
        }
​
        // 否则的话，将当前元素作为索引，当前元素的下标作为值，填入数组中，
        // 方便后续的查找判重
        bucket[nums[i]] = i;
    }
    return -1;
    时间复杂度是 O(n)
    空间复杂度是 O(n)
}


主要用于重复出现的数，缺失的数等题目中，下面我们看一下这个原地置换法，
原地置换的大体思路就是将我们指针对应的元素放到属于他的位置（索引对应的地方）。
我们可以这样理解，每个人都有自己的位置，我们需要和别人调换回到属于自己的位置，
调换之后，如果发现我们的位置上有人了，则返回。

public int findRepeatNumber(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        while (i != nums[i]) {
​
            if (nums[i] == nums[nums[i]]) {
                return nums[i];
            }
​
            // 交换
            int tmp = nums[nums[i]];
            nums[nums[i]] = nums[i];
            nums[i] = tmp;
        }
    }

    return -1;
    时间复杂度是 O(n)
    空间复杂度是 O(1)
}

* */